﻿// ===============================================================================
// Control.cs
// EOS Web Framework - Demo
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Web.Mvc
{
    using System;
    using System.Collections.Generic;
    using EOS.Framework.Tools.Aspects;

    /// <summary>
    /// This is the base class for all controls.
    /// </summary>
    public abstract class Control
    {
        /// <summary>
        /// List of all assigned attributes and values to this control.
        /// </summary>
        private Dictionary<string, Attribute> _attributes = new Dictionary<string, Attribute>();
        /// <summary>
        /// True, if the control is in render writing mode.
        /// </summary>
        private bool _writingStarted;
        /// <summary>
        /// True, if the control is already rendered, else false.
        /// </summary>
        private bool _rendered;

        /// <summary>
        /// Occurs when the control is attached to a markup.
        /// </summary>
        public event EventHandler Attached;

        /// <summary>
        /// Represents an xhtml attribute and its values of this control.
        /// </summary>
        class Attribute
        {
            /// <summary>
            /// The name of the attribute.
            /// </summary>
            public string Name;

            /// <summary>
            /// Its value.
            /// </summary>
            public string Value;
        }

        #region Properties

        private IMarkup _parent;

        private string _key;

        /// <summary>
        /// Gets or sets the key.
        /// </summary>
        /// <value>The key of the markup.</value>
        public string Key
        {
            get { return _key; }
        }  

        /// <summary>
        /// Gets or sets the parent markup object, where this control
        /// is assigned to.
        /// </summary>
        /// <value>The parent markup object. Cannot be null (Nothing in Visual Basic).</value>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null
        /// (Nothing in Visual Basic).</exception>
        [NotNull]
        public IMarkup Parent
        {
            get { return _parent; }
        }
        
        private string _tagName;

        /// <summary>
        /// Gets or sets the name of the tag.
        /// </summary>
        /// <value>The name of the tag. Cannot be null or empty and
        /// cannot have only blanks.</value>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is null
        /// (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException">
        ///     <para><paramref name="value"/> has only blanks.</para>
        ///     <para><paramref name="value"/> is a string of length 0.</para>
        /// </exception>
        [NotNullOrEmpty]
        public string TagName
        {
            set { _tagName = value; }
            get { return _tagName; }
        }

        #endregion

        [ParamNotNullOrEmpty("name")]
        public void AddAttribute(string name, string value)
        {
            if (!_rendered)
            {
                Attribute storage = new Attribute();
                storage.Name = name;
                storage.Value = value;

                _attributes.Add(name, storage);
            }
        }

        [ParamNotNullOrEmpty("name")]
        protected bool VerifyAttributeHasValue(string name, string value)
        {
            return _attributes.ContainsKey(name) && _attributes[name].Value == value;
        }

        [ParamNotNullOrEmpty("name")]
        protected string GetAttributeValue(string name)
        {
            if (_attributes.ContainsKey(name))
            {
                return _attributes[name].Value;
            }
            else
            {
                return null;
            }
        }

        #region Rendering

        /// <summary>
        /// Called when the control should be rendered.
        /// </summary>
        /// <param name="writer">The writer for writing xhtml.</param>
        /// <param name="context">The context object, which contains context values of the current page.</param>
        protected abstract void OnRender(IXhtmlWriter writer, PageContext context);

        /// <summary>
        /// Called before rendering.
        /// </summary>
        protected virtual void OnBeforeRender()
        {
        }

        public void Render(IXhtmlWriter writer, PageContext context)
        {
            OnBeforeRender();

            _rendered = true;
            OnRender(writer, context);
        }

        protected virtual void WriteBeginAttachedTag(IXhtmlWriter writer)
        {
            WriteBeginAttachedTag(writer, null);
        }

        protected virtual void WriteBeginAttachedTag(IXhtmlWriter writer, string[] attributeFilter)
        {
            if (writer == null)
            {
                throw new ArgumentException("writer");
            }

            _writingStarted = true;

            writer.WriteStartElement(_tagName);
            foreach (Attribute storage in _attributes.Values)
            {
                bool filtered = false;

                if (attributeFilter != null)
                {
                    foreach (string filter in attributeFilter)
                    {
                        if (filter == storage.Name)
                        {
                            filtered = true;
                        }
                    }
                }

                if (!filtered)
                {
                    writer.WriteAttribute(storage.Name, storage.Value);
                }
            }
        }

        protected void WriteEndAttachedTag(IXhtmlWriter writer)
        {
            if (_writingStarted == false)
            {
                // Throw better exception there.
                throw new Exception();
            }

            if (writer == null)
            {
                throw new ArgumentException("writer");
            }

            writer.WriteEndElement();
        }

        protected void WriteEndAttachedTagFull(IXhtmlWriter writer)
        {
            if (_writingStarted == false)
            {
                // Throw better exception there.
                throw new Exception();
            }

            if (writer == null)
            {
                throw new ArgumentException("writer");
            }

            writer.WriteEndElement();
        } 

        #endregion

        internal void NotifyAttached(IMarkup parent, string key)
        {
            _key = key;
            _parent = parent;

            OnAttached();
        }

        protected virtual void OnAttached()
        {
            if (Attached != null)
            {
                Attached(this, EventArgs.Empty);
            }
        }

        [ParamNotNullOrEmpty("path")]
        protected string ToAbsolutePath(string path)
        {
            return UrlPath.ToAbsolutPath(path);
        }
    }
}
